#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @Project:     rest_flask
# @File:        logic_matcher.py
# @Author:      Fan GuiChuan
# @Date:        2025/6/30 16:03
# @Version:     Python3.7
# @Description: 逻辑表达式匹配器（支持中缀表达式转后缀表达式并求值，支持命中操作数统计）

from typing import List, Set, Union, Tuple, Dict, Any


class LogicMatcher:
    def __init__(self, logic_rules: List[List[str]], logic_rule_ids):
        """初始化逻辑匹配器
        Args:
            logic_rules: 逻辑规则列表，每个规则是token列表，如 ['1', '&', '2']
            logic_rule_ids: 规则ID列表
        """
        self.logic_rule_ids = logic_rule_ids
        self.logic_rules: List[List[str]] = []
        self.rule_variables: List[Set[str]] = []  # 存储每个规则的变量集合

        for rule in logic_rules:
            try:
                postfix = self._infix_to_postfix(rule)
                self.logic_rules.append(postfix)
                # 提取规则中的所有变量
                variables = {token for token in rule if self.is_op_number(token)}
                self.rule_variables.append(variables)
            except ValueError as e:
                print(f"规则 {rule} 转换错误: {str(e)}")
                self.logic_rules.append([])  # 用空列表标记无效规则
                self.rule_variables.append(set())

    def match_all(self, data: List[Union[str, int]]) -> List[dict]:
        """匹配所有规则
        Args:
            data: 输入的数字列表（可以是字符串或整数）
        Returns:
            匹配成功的规则信息列表，包含命中操作数个数
        """
        results: List[dict] = []
        # 统一转换为字符串集合
        data_set: Set[str] = {str(x) for x in data}

        for idx, postfix_expr in enumerate(self.logic_rules):
            if not postfix_expr:  # 跳过无效规则
                continue

            try:
                # 计算表达式结果和命中操作数
                result, hit_count = self._evaluate_postfix_with_count(postfix_expr, data_set)

                if result:
                    rule_info = self.logic_rule_ids[idx]
                    results.append({
                        'feature_id': rule_info['label_id'],
                        'feature_name': rule_info['label_name'],
                        'level_code': rule_info.get('level_code', ''),
                        'level_id': rule_info.get('level_id', 0),
                        'rule_id': rule_info['rule_id'],
                        'template_id': rule_info['template_id'],
                        'from': 0,
                        'to': 0,
                        'match': '',  # 提取匹配文本
                        'identify_type_name': 'logic',
                        'identify_type': 0,
                        'hit_count': hit_count,  # 新增：命中操作数个数
                        'total_variables': len(self.rule_variables[idx])  # 新增：规则总操作数
                    })
            except Exception as e:
                print(f"规则 {postfix_expr} 评估错误: {str(e)}")

        return results

    def get_hit_count_for_rule(self, rule_idx: int, data: List[Union[str, int]]) -> Tuple[bool, int]:
        """获取特定规则的匹配结果和命中操作数个数
        Args:
            rule_idx: 规则索引
            data: 输入数据
        Returns:
            (是否匹配, 命中操作数个数)
        """
        if rule_idx >= len(self.logic_rules) or not self.logic_rules[rule_idx]:
            return False, 0

        data_set: Set[str] = {str(x) for x in data}
        try:
            result, hit_count = self._evaluate_postfix_with_count(
                self.logic_rules[rule_idx], data_set
            )
            return result, hit_count
        except Exception as e:
            print(f"规则 {rule_idx} 评估错误: {str(e)}")
            return False, 0

    def _evaluate_postfix_with_count(self, postfix_expr: List[str], data_set: Set[str]) -> Tuple[bool, int]:
        """评估后缀表达式并返回命中操作数个数
        Args:
            postfix_expr: 后缀表达式token列表
            data_set: 输入数据集合（字符串形式）
        Returns:
            (布尔值结果, 命中操作数个数)
        Raises:
            ValueError: 当表达式无效时抛出
        """
        stack: List[Union[bool, Tuple[bool, int]]] = []

        for token in postfix_expr:
            # 处理数字
            if self.is_op_number(token):
                is_present = token in data_set
                # 存储 (结果, 命中数)
                stack.append((is_present, 1 if is_present else 0))

            # 处理NOT运算符（一元）
            elif token == '!':
                if not stack:
                    raise ValueError("NOT运算符缺少操作数")
                operand, hit_count = stack.pop()
                stack.append((not operand, hit_count))  # NOT不改变命中数

            # 处理二元运算符
            elif token in ('&', '|'):
                if len(stack) < 2:
                    raise ValueError(f"{token} 运算符缺少操作数")
                right, right_count = stack.pop()
                left, left_count = stack.pop()

                if token == '&':
                    result = left and right
                    # AND操作：如果结果为真，命中数是两个操作数命中数之和
                    hit_count = left_count + right_count if result else 0
                else:  # OR
                    result = left or right
                    # OR操作：命中数是两个操作数命中数之和（可能有重叠，但这里简单相加）
                    hit_count = left_count + right_count

                stack.append((result, hit_count))

            else:
                raise ValueError(f"无效的token: {token}")

        if len(stack) != 1:
            raise ValueError(f"表达式无效，栈中剩余 {len(stack)} 个元素")

        result, hit_count = stack[0]
        return result, hit_count

    def get_rule_variables(self, rule_idx: int) -> Set[str]:
        """获取规则中的所有变量
        Args:
            rule_idx: 规则索引
        Returns:
            变量集合
        """
        if 0 <= rule_idx < len(self.rule_variables):
            return self.rule_variables[rule_idx]
        return set()

    def get_all_hit_variables(self, rule_idx: int, data: List[Union[str, int]]) -> Set[str]:
        """获取规则中所有命中的变量
        Args:
            rule_idx: 规则索引
            data: 输入数据
        Returns:
            命中的变量集合
        """
        if rule_idx >= len(self.rule_rules) or not self.logic_rules[rule_idx]:
            return set()

        data_set = {str(x) for x in data}
        rule_vars = self.rule_variables[rule_idx]
        return rule_vars & data_set  # 交集即为命中的变量

    @staticmethod
    def is_op_number(token):
        """判断token是否为操作数（非运算符）"""
        return token not in ['&', '|', '!', '(', ')']

    def _infix_to_postfix(self, infix_tokens: List[str]) -> List[str]:
        """中缀表达式转后缀表达式（调度场算法）"""
        # 保持原有实现不变
        precedence = {'!': 3, '&': 2, '|': 1}
        output: List[str] = []
        operator_stack: List[str] = []

        for token in infix_tokens:
            token = token.strip()
            if not token:
                continue

            if self.is_op_number(token):
                output.append(token)
            elif token == '(':
                operator_stack.append(token)
            elif token == ')':
                while operator_stack and operator_stack[-1] != '(':
                    output.append(operator_stack.pop())
                if not operator_stack:
                    raise ValueError("括号不匹配")
                operator_stack.pop()
            elif token in precedence:
                if token == '!':
                    operator_stack.append(token)
                else:
                    while (operator_stack and operator_stack[-1] != '(' and
                           precedence[operator_stack[-1]] >= precedence[token]):
                        output.append(operator_stack.pop())
                    operator_stack.append(token)
            else:
                raise ValueError(f"无效的token: {token}")

        while operator_stack:
            if operator_stack[-1] == '(':
                raise ValueError("括号不匹配")
            output.append(operator_stack.pop())

        return output

    @staticmethod
    def parse_rule_to_tokens(rule_str):
        """
        将规则字符串解析为标记列表
        例如: "(标签1&标签2)|(!标签3)" -> ["(", "标签1", "&", "标签2", ")", "|", "(", "!", "标签3", ")"]
        """
        tokens = []
        i = 0
        n = len(rule_str)
        while i < n:
            if rule_str[i] in '()&|!':
                tokens.append(rule_str[i])
                i += 1
            elif rule_str[i].isspace():
                i += 1
            else:
                j = i
                while j < n and rule_str[j] not in '()&|! ' and not rule_str[j].isspace():
                    j += 1
                token = rule_str[i:j].strip()
                if token:
                    tokens.append(token)
                i = j
        return tokens


# 测试代码
if __name__ == '__main__':
    # 测试用例
    rules = [
        ["4", "&", "5"],  # 4 AND 5
        ["11zd", "&", "2"],  # 11zd AND 2
        ["1", "|", "3"],  # 1 OR 3
        ["!", "(", "1", "&", "2", ")"],  # NOT (1 AND 2)
        ["(", "1", "|", "2", ")", "&", "3"],  # (1 OR 2) AND 3
        ["1", "&", "!", "2", "|", "3"]  # 1 AND NOT 2 OR 3
    ]

    rule_ids = [
        {'label_id': 1, 'label_name': '规则1', 'rule_id': 1, 'template_id': 1},
        {'label_id': 2, 'label_name': '规则2', 'rule_id': 2, 'template_id': 1},
        {'label_id': 3, 'label_name': '规则3', 'rule_id': 3, 'template_id': 1},
        {'label_id': 4, 'label_name': '规则4', 'rule_id': 4, 'template_id': 1},
        {'label_id': 5, 'label_name': '规则5', 'rule_id': 5, 'template_id': 1},
        {'label_id': 6, 'label_name': '规则6', 'rule_id': 6, 'template_id': 1}
    ]

    matcher = LogicMatcher(rules, rule_ids)

    # 测试不同输入
    test_cases = [
        ['4', '5'],
        ['11zd', '2'],
        ['1', '3'],
        ['2', '3'],
        ['1', '2', '3'],
        ['4']
    ]

    for i, test_data in enumerate(test_cases):
        print(f"\n测试数据 {i + 1}: {test_data}")
        results = matcher.match_all(test_data)

        for result in results:
            print(
                f"  规则 {result['feature_name']}: 匹配={True}, 命中数={result['hit_count']}/{result['total_variables']}")

        # 测试单个规则命中数获取
        for rule_idx in range(len(rules)):
            matched, hit_count = matcher.get_hit_count_for_rule(rule_idx, test_data)
            if matched:
                print(f"  规则{rule_idx + 1}: 命中数={hit_count}")

    # 测试解析函数
    rule_str = "(标签1&标签2)|(!标签3)"
    tokens = LogicMatcher.parse_rule_to_tokens(rule_str)
    print(f"\n解析规则: {rule_str} -> {tokens}")